// -----------------------------------------------------------------------------
// Author:  Doug Kaiser
// Class:  CS361
// Assignment:  Week 1, Program 1, Speeding Ticket Fine Calculator
// Program Description:  Processes speeding ticket info and determines fines and
//          creates reports
//   Input: Speeding ticket info from a file
//   Processing:  Reads file and processes data
//   Output:  QUADFINES.TXT, CARFINES.TXT, Car and ticket data, and totals
// -----------------------------------------------------------------------------

#include <iostream>                     // I/O
#include <iomanip>                      // Format I/O
#include <fstream>                      // File I/O
#include <string>                       // For string manip

using namespace std;

// Function Prototypes
void PrintHeader();
void ReadTickets(ifstream& ticketData, ofstream& quadData, ofstream& carData);
void TicketHandler(string ticketInfo, ofstream& quadData, ofstream& carData);
double FineCalc(int speedLimit, int clockedSpeed);
void ColumnHeaders();
void DisplayData(string license, int numTickets, double fines);
void DisplayTotal();
void WriteQuadFines(ofstream& quadData, string quad, double fines);
void WriteCarFines(ofstream& carData, string licensePlate, double fines);

// Global Constant Declaration
const double COURT_COSTS = 85.00;
const double LOW_FINE = 2.25;
const double MID_FINE = 3.50;
const double HIGH_FINE = 5.00;

const int LOW_SPEED = 10;
const int HIGH_SPEED = 20;

const int TOTAL_FLAG = -10;             // Picked -10, because you can't go negative MPH
const int TICKET_CHARS = 6;             // With no spaces there are 6 characters
const int TICKET_TOKENS = 3;            // Each ticket has 3 pieces of data minus the license plate

const int FULL_SCREEN = 22;             // Could be 24, but 22 looks better

const int PLATE_LENGTH = 6;             // Length of a license plate

// -----------------------------------------------------------------------------
// Function main calls our functions to process tickets
//   Returns: 0 if sucessful
// -----------------------------------------------------------------------------
int main() {

    // Variable Declaration
    ifstream ticketData;
    ofstream quadFines;
    ofstream carFines;

    // Open our data files
    ticketData.open("TICKETS.txt");
    quadFines.open("QUADFINES.txt");
    carFines.open("CARFINES.txt");

    // Print our header
    PrintHeader();

    // If we can open TICKETS.TXT
    if (ticketData) {

        // Call our function to read tickets
        ReadTickets(ticketData, quadFines, carFines);

        // Once we are done with processing tickets, display total
        DisplayTotal();

    // TICKETS.TXT could not be opened
    } else {

        // Print error message
        cout << "Error reading TICKETS.TXT" << endl;

    }

    // Close our data files
    ticketData.close();
    quadFines.close();
    carFines.close();

    // Standard closing code
    system("PAUSE");
    return 0;                           // 0 if sucessful
}

// -----------------------------------------------------------------------------
// Function PrintHeader prints our header
// -----------------------------------------------------------------------------
void PrintHeader() {

    // Print our nice header
    cout << "OurCity Police Department Speeding Ticket Database" << endl << endl;
}

// -----------------------------------------------------------------------------
// Function ReadTickets processes each line in our input file
//   Input:  TicketData, QuadData, CarData
// -----------------------------------------------------------------------------
void ReadTickets(ifstream& ticketData, ofstream& quadData, ofstream& carData) {

    // variable declaration
    char newLine;
    string ticketToken;
    string ticketInformation = "";

    // "Prime" our reader
    ticketData >> ticketToken;

    // Run until we hit the end of the file
    while (ticketData) {

        // run until we hit the end of a line
        while(newLine != '\n') {

            // Create one line of ticket information with no spaces
            ticketInformation += ticketToken;

            // check for a new line
            newLine = ticketData.peek();

            // Get more data
            ticketData >> ticketToken;
    
        }

        // Call the function to handle the ticket data
        TicketHandler(ticketInformation, quadData, carData);

        // Reset our variables
        ticketInformation = "";
        newLine = ticketData.peek();
    }
}

// -----------------------------------------------------------------------------
// Function TicketHandler determines specific ticket information
//   Input:  ticket info, quaddata, and car data
// -----------------------------------------------------------------------------
void TicketHandler(string ticketInfo, ofstream& quadData, ofstream& carData) {

    // Variable declarations
    string licensePlate;
    int numberOfTickets;
    double totalFines;
    string ticketData;
    int infoThree;
    int infoTwo;
    string infoOne;
    int charsLeft = ticketInfo.length() - PLATE_LENGTH;
    double fines;

    // Determine the number of tickets, but how long the string is
    numberOfTickets = (ticketInfo.length() - TICKET_CHARS)/(TICKET_TOKENS * 2);

    // License plate info is the first 6 chars of the string
    licensePlate = ticketInfo.substr(0, PLATE_LENGTH);

    // Run until the entire line of data has been processed
    for (int count = charsLeft; count > 0; count -= TICKET_CHARS) {

        // Stores only ticket data, not car data
        ticketData = ticketInfo.substr(count, 6);

        // Pull specifics from our ticket data, backwards
        // Clocked Speed
        infoThree = atoi(ticketData.substr(ticketData.length()-2,2).c_str());

        // Speed limit
        infoTwo = atoi(ticketData.substr(ticketData.length() -4,2).c_str());

        // Quadrant
        infoOne = ticketData.substr(ticketData.length()-6,2);

        // Keep determine the fine
        fines = FineCalc(infoTwo, infoThree);
        
        // Write Quad data
        WriteQuadFines(quadData, infoOne, fines);
        
        // Write car data
        WriteCarFines(carData, licensePlate, fines);
        
        // Keep a total of all fines per car
        totalFines += fines;
    }

    // Call our function to display this data
    DisplayData(licensePlate, numberOfTickets, totalFines);

    


}

// -----------------------------------------------------------------------------
// Function ColumnHeaders prints our column headers
// -----------------------------------------------------------------------------
void ColumnHeaders() {

    // Just prints our headers to the screen

    cout << setw(11) << "";

    cout << setw(11) << left << "Number";

    cout << setw(10) << left << "Total";

    cout << endl;

    cout << setw(11) << left << "License";

    cout << setw(11) << left << "Tickets";

    cout << setw(10) << left << "Fines";

    cout << endl;

    cout << setw(11) << left << "-------";

    cout << setw(11) << left << "-------";

    cout << setw(10) << left << "----------";

    cout << endl;
}

// -----------------------------------------------------------------------------
// Function FineCalc calculates ticket fines
//   Input:  speed limit, and clocked speed
//   Returns: return value
// -----------------------------------------------------------------------------
double FineCalc(int speedLimit, int clockedSpeed) {

    // Variable declaration
    int difference = clockedSpeed - speedLimit;
    static double totalFines;
    double returnValue;
    double fines;

    // Use difference to determine the fines that should be charged
    if (difference <= LOW_SPEED)

        // Lowest Fines
        fines = COURT_COSTS + LOW_FINE * difference;

    else if (difference > LOW_SPEED && difference <= HIGH_SPEED)

        // Fines are in the middle
        fines = COURT_COSTS + MID_FINE * difference;

    else

        // Highest Fines
        fines = COURT_COSTS + HIGH_FINE * difference;

    // Determines if we should return the per ticket fine or the total fines
    if (speedLimit == TOTAL_FLAG && clockedSpeed == TOTAL_FLAG) {

        // Return total fines
        returnValue = totalFines;

    } else {
        
        // Adds each tickets fines to total and stores the tickets fine
        totalFines += fines;
        returnValue = fines;

    }

    // Return either total fines or per ticket
    return returnValue;
}

// -----------------------------------------------------------------------------
// Function DisplayData displays each tickets data to the user
//   Input:  License, Number of tickets and fines
// -----------------------------------------------------------------------------
void DisplayData(string license, int numTickets, double fines) {

    // Variable declarations
    static int numberOfLines = 0;

    // If our screen is full
    if (numberOfLines == FULL_SCREEN) {

        // Create some space
        cout << endl;

        // Wait for user to finish reading
        system("PAUSE");

        // Prime number of lines (2 is technically incorrect, but for display
        // sake it makes the output look better)
        numberOfLines = 2;

        // More space (I like space)
        cout << endl;

        // Print Column Headers again
        ColumnHeaders();
    }


    // If there are no lines, then this is the first time ticket info has
    // been printed
    if (numberOfLines == 0) {

        // Print Column headers
        ColumnHeaders();

        // Print ticket info
        cout << setw(11) << left << license;
        cout << setw(3) << left << "";
        cout << setw(8) << left << numTickets;
        cout << setw(1) << left << "$";
        cout << setw(9) << right << fixed << showpoint << setprecision(2)<< fines << endl;

        // Add 6 lines to our running total
        numberOfLines += 6;
    } else {

        // This is not the first time we are printing so just print the ticket
        // info
        cout << setw(11) << left << license;
        cout << setw(3) << left << "";
        cout << setw(8) << left << numTickets;
        cout << setw(1) << left << "$";
        cout << setw(9) << right << fixed << showpoint << setprecision(2)<< fines << endl;

        // Add 1 to our running total
        numberOfLines++;
    }
}

// -----------------------------------------------------------------------------
// Function DisplayTotal displays the grand total of all tickets
// -----------------------------------------------------------------------------
void DisplayTotal() {

    // Format the line that will hold the grand total
    cout << setw(22) << left << "";
    cout << setw(10) << right << "----------" << endl;
    cout << setw(22) << left << "Total Fines";
    cout << setw(1) << left << "$";

    // Call our fine handling function and pass it some values that flag it to
    // return the grand total
    cout << setw(9) << right << FineCalc(TOTAL_FLAG, TOTAL_FLAG) << endl;
}

// -----------------------------------------------------------------------------
// Function WriteQuadFines writes quad data to text file
//   Input:  quad data, quad, and fines
// -----------------------------------------------------------------------------
void WriteQuadFines(ofstream& quadData, string quad, double fines){

    // Check to see if we can write to this file
    if (quadData) {

        // IF so, write our quad data to the file
        quadData << setw(4) << left << quad;
        quadData << setw(8) << right << fixed << showpoint << setprecision(2) << fines;
        quadData << endl;
    } else {

        // Otherwise give an error
        cout << "Error writing to QUADFINES.TXT" << endl;
    }
}

// -----------------------------------------------------------------------------
// Function WriteCarFines writes car data to text file
//   Input:  car data, license plate, and fines
// -----------------------------------------------------------------------------
void WriteCarFines(ofstream& carData, string licensePlate, double fines) {

    // Check to see if we can write to this file
    if (carData) {

        // If so, write the data to the file
        carData << setw(8) << left << licensePlate;
        carData << setw(8) << right << fixed << showpoint << setprecision(2) << fines;
        carData << endl;
    } else {

        // Otherwise an error is printed
        cout << "Error writing to CARFINES.TXT" << endl;
    }
}

